/**
  接口能够描述JavaScript中对象拥有的各种各样的外形。 除了描述带有属性的普通对象外，接口也可以描述函数类型。
  Interfaces are capable of describing the wide range of shapes that JavaScript objects can take. In addition to describing an object with properties, interfaces are also capable of describing function types.

  为了使用接口表示函数类型，我们需要给接口定义一个调用签名。 它就像是一个只有参数列表和返回值类型的函数定义。参数列表里的每个参数都需要名字和类型。
  To describe a function type with an interface, we give the interface a call signature. This is like a function declaration with only the parameter list and return type given. Each parameter in the parameter list requires both name and type.
    + 对于函数类型的类型检查来说，函数的参数名不需要与接口里定义的名字相匹配,只会要求对应位置上的参数类型是兼容的
*/
interface 二则运算 {
  (a:number, b:number):number //
}

// let add: 二则运算 = function (d: number, e: number): number {
//   return d + e;
// };
/* ↑
你也可以不用在赋值时再在源函数上手动写上类型,因为add变量已经被 二则运算 这个类型赋值了,typescript可以推断出来
If you do not want to specify types at all, TypeScript’s contextual typing can infer the argument types since the function value is assigned directly to a variable of type SearchFunc(←我们这里是`二则运算`).
像这样↓*/
// let add: 二则运算 = function (d, e) {
//   return d + e;
// };

// 参数类型和函数返回值类型必须符合我们接口所定义的类型
let add: 二则运算 = function (c){ // 我们规定了 二则运算 要接受两个参数,故这里赋的值其实是错误的 不会红线 但当我们调用该函数时typescript会报错()
  return c
};

// add(1); // TS2554: Expected 2 arguments, but got 1.



/** Hybrid Types 混合类型*/
// 我们说过 接口就是用代码描述一个对象必须有什么属性(包括方法)
// 故我们也能为一个函数接口定义属性
interface 二则运算2 {
  (a: number, b: number): number;
  逆运算(a: number, b: number): number;
}

//TS2741: Property '逆运算' is missing in type '(a: number, b: number) => void' but required in type '二则运算2'.
//↕
// let add2: 二则运算2 = function (a, b) {
//
// };
//↑ 这就有一个问题 我们怎么赋值呢？
let add2: 二则运算2 = (
  ()=>{
    let x: any = function (a, b) {
      return a + b;
    };
    x.逆运算 = function (a, b) {
      return a - b;
    };
    x.other = '123'; // 并不会报错 不是我们期望的
    return x;
  }
)();


// 换一种写法
interface Lib {
  ():void;
  version:string;
  doSomething():void;
}

function getLib():Lib{
  let lib:Lib = (()=>{}) as Lib; /** 这里的as和上面的 any相比 能起到"限制"的作用 多写属性就会报错 但是少些它并不会报错 因为我们已经断言了*/
  lib.version = '1.0';
  lib.doSomething = ()=>{};
  // lib.other = 'xxx'; //TS2339: Property 'other' does not exist on type 'Lib'.
  return lib;
}

let lib1 = getLib();
lib1();
lib1.doSomething();

let lib2 = getLib();


// 再换一种写法
interface Counter {
  (start: number): string;
  interval: number;
  reset(): void;
}

function getCounter(): Counter {
  let counter = <Counter>function (start: number) { };
  counter.interval = 123;
  counter.reset = function () { };
  return counter;
}

let c = getCounter();
c(10);
c.reset();
c.interval = 5.0;

export {}
